Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Feature/xpress solver for mathopt #137

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

pet-mit
Copy link
Collaborator

@pet-mit pet-mit commented Nov 28, 2024

No description provided.

ortools/math_opt/solvers/xpress/g_xpress.cc Outdated Show resolved Hide resolved
ortools/math_opt/solvers/xpress/g_xpress.cc Outdated Show resolved Hide resolved
ortools/math_opt/solvers/xpress/g_xpress.cc Outdated Show resolved Hide resolved
const absl::Span<const double> ub,
const absl::Span<const char> vtype) {
CHECK_EQ(vind.size(), vval.size());
const int num_vars = static_cast<int>(lb.size());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are not quite ready yet, but we are working up to int64 support (models with more than 2**31 variables). Not sure if xpress supports this, but if you do, maybe use int64_t instead of int here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XPRESS does support this but through separate API calls of course (in this instance, XPRSaddcols64 instead of XPRSaddcols). If it's OK for you, I'll add a TODO here to look into it, and add support for int64 later (it will need some reflexion on how to support both 32 and 64 archs, maybe we'll wait for you to do it for gurobi and then copy)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Xpress supports only 64bit counts of non-zeros. Number of variables, rows, etc. are all limited to 32bit signed integers. Since you are not adding any non-zeros here, the code is already safe for the kind of 64bit stuff that Xpress supports.
I still suggest that instead of static_cast<int>(lb.size()) you use a function that checks for overflow (lb.size() larger than INT_MAX) and raises an error if a user tries to create that many variables. I guess such a function will come in handy in other places as well.

ortools/math_opt/solvers/xpress/g_xpress.cc Outdated Show resolved Hide resolved
ortools/math_opt/solvers/xpress_solver_test.cc Outdated Show resolved Hide resolved
ortools/math_opt/solvers/xpress_solver.cc Outdated Show resolved Hide resolved
ortools/math_opt/solvers/xpress_solver.cc Outdated Show resolved Hide resolved
ortools/math_opt/solvers/xpress_solver.cc Outdated Show resolved Hide resolved
ortools/math_opt/solvers/xpress_solver.cc Show resolved Hide resolved
Copy link

@djunglas djunglas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a million for doing this!
I made a first review pass over the implementation (not the tests). In my comments I assume that we want to code against a somewhat recent version of Xpress (that already has XPRSoptmize(), XPRSgetsolution(), XPRSgetduals(), ...).

return absl::OkStatus();
}
char errmsg[512];
XPRSgetlasterror(xpress_model_, errmsg);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory XPRSgetlasterror() can fail. To make this rock solid I suggest to check the return value of that function and set errmsg to some generic string in case the function returns non-zero.

bool correctlyLoaded = initXpressEnv();
CHECK(correctlyLoaded);
XPRSprob* model;
CHECK_EQ(kXpressOk, XPRScreateprob(model));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks wrong to me. XPRSprob already is a pointer type. I think this should read

XPRSprob model;
CHECK_EQ(kXpressOk, XPRScreateprob(&model));
DCHECK(model != nullptr);
return absl::WrapUnique(new Xpress(model));

const absl::Span<const double> ub,
const absl::Span<const char> vtype) {
CHECK_EQ(vind.size(), vval.size());
const int num_vars = static_cast<int>(lb.size());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Xpress supports only 64bit counts of non-zeros. Number of variables, rows, etc. are all limited to 32bit signed integers. Since you are not adding any non-zeros here, the code is already safe for the kind of 64bit stuff that Xpress supports.
I still suggest that instead of static_cast<int>(lb.size()) you use a function that checks for overflow (lb.size() larger than INT_MAX) and raises an error if a user tries to create that many variables. I guess such a function will come in handy in other places as well.


const int n_cols = static_cast<int>(colind.size());
auto c_colind = const_cast<int*>(colind.data());
auto c_values = const_cast<double*>(values.data());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why const_cast? There is no need to cast away the const since XPRSchgobj() takes const arguments.

const int n_coefs = static_cast<int>(rowind.size());
auto c_rowind = const_cast<int*>(rowind.data());
auto c_colind = const_cast<int*>(colind.data());
auto c_values = const_cast<double*>(values.data());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, no need to cast away const. You should be able to directly pass rowind.data() etc. to XPRSchgmcoef().
Here I suggest to use XPRSchgmcoef64(). All data is already in the right format. You only have to change n_coefs to a 64bit integer. This then supports 64bit non-zero counts.

return xpress_->AddConstrs(constraint_sense, constraint_rhs, constraint_rng);
}

absl::Status XpressSolver::AddSingleObjective(const ObjectiveProto& objective) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this function supposed to do? At the moment (unless I am mistaken) it only changes the coefficients of variables that appear in objective. If a variable is not in objective then its objective coefficient won't be changed. In particular, it would not be zeroed. Or is objective always dense?

}
// Post-solve
if (!(is_mip_ ? (xpress_status_ == XPRS_MIP_OPTIMAL)
: (xpress_status_ == XPRS_LP_OPTIMAL))) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I commented in the other file, the preferred way to solve is XPRSoptimize() and then just check the solve and solution status returned by this function.

int lp_iter_limit = parameters.has_iteration_limit()
? parameters.iteration_limit()
: 2147483647;
// XPRESS default value for XPRS_BARITERLIMIT is 500

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to get the default values, you should rather query the attributes right after calling XPRScreateprob(). This seems more future proof.

}

absl::StatusOr<double> XpressSolver::GetBestDualBound() const {
// TODO: setting LP primal value as best dual bound. Can this be improved?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is XPRS_BESTBOUND for this.


bool XpressSolver::isFeasible() const {
if (is_mip_) {
return xpress_status_ == XPRS_MIP_OPTIMAL;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is another status that indicates "feasible but not proven optimal": XPRS_MIP_SOLUTION, see here.

@pet-mit
Copy link
Collaborator Author

pet-mit commented Dec 11, 2024

Thanks a million for doing this! I made a first review pass over the implementation (not the tests). In my comments I assume that we want to code against a somewhat recent version of Xpress (that already has XPRSoptmize(), XPRSgetsolution(), XPRSgetduals(), ...).

Hi Daniel!

Thanks a lot for you valuable review!
You are right, we also prefer to target newer versions of XPRESS, and I'm definitely not familiar with the newer vs the older API calls.
I will take all your comments into account; however I'm on my way to my annual holidays until january, so I will do this in 2025 :)

Best regards !
Peter

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants